using Gavel.Common.Utilities.Const;

namespace Gavel.Common.Utilities.Extension;

/// <summary>
/// 进制/类型转换扩展类
/// </summary>
public static class ConvertExtension
{
    /// <summary>
    /// 字节数组转16进制串
    /// </summary>
    /// <param name="bytes">字节数组</param>
    /// <returns>16进制串</returns>
    public static string ToHex(this byte[] bytes) => BitConverter.ToString(bytes).Replace("-", string.Empty);

    /// <summary>
    /// 字节数组转16位无符号整型
    /// </summary>
    /// <param name="bytes">字节数组</param>
    /// <param name="endian">端类型</param>
    /// <returns>16位无符号整型</returns>
    public static ushort ToUshort(this byte[] bytes, Endian endian = Endian.Big) =>
        bytes.Length < 2
            ? throw new ArgumentOutOfRangeException(nameof(bytes), "字节数组长度不足2位，无法转换为16位无符号整型")
            : BitConverter.ToUInt16
            (
                (BitConverter.IsLittleEndian && endian == Endian.Little) || (BitConverter.IsLittleEndian == false && endian == Endian.Big)
                    ? bytes
                    : bytes.Reverse().ToArray(),
                0
            );

    /// <summary>
    /// 将字节数组转为二进制串
    /// </summary>
    /// <param name="bytes">2位字节数组</param>
    /// <returns>二进制串</returns>
    public static string ToBinary(this byte[] bytes) => string.Join("", bytes.Select(b => Convert.ToString(b, 2).PadLeft(8, '0')));

    /// <summary>
    /// 无符号16位整型转字节数组
    /// </summary>
    /// <param name="value">无符号16位整型</param>
    /// <param name="endian">大小端（默认小端）</param>
    /// <returns>字节数组</returns>
    public static byte[] ToBytes(this ushort value, Endian endian = Endian.Big) =>
        BitConverter.IsLittleEndian
            ? endian == Endian.Little
                ? BitConverter.GetBytes(value)
                : BitConverter.GetBytes(value).Reverse().ToArray()
            : endian == Endian.Little
                ? BitConverter.GetBytes(value).Reverse().ToArray()
                : BitConverter.GetBytes(value);

    /// <summary>
    /// ModbusRTU的CRC校验码计算
    /// </summary>
    /// <param name="data">数据域数据</param>
    /// <param name="endian">大小端</param>
    /// <returns>CRC校验码</returns>
    public static byte[] CalculateCrc(this byte[] data, Endian endian = Endian.Little)
    {
        ushort crc = 0xFFFF; // 初始化CRC值
        foreach (byte b in data)
        {
            crc ^= b; // 将当前字节与CRC值进行异或

            for (int i = 0; i < 8; i++) // 对每个比特进行处理
            {
                bool carry = (crc & 0x0001) == 0x0001; // 检查最低位
                crc >>= 1; // 右移一位
                if (carry) // 如果最低位是1，则与多项式进行异或  
                {
                    crc ^= 0xA001;
                }
            }
        }

        return crc.ToBytes(endian); // 返回计算得到的CRC值
    }
}